/****************************************************************************
 *  _____       ______  _____
 * |_   _|     |  ____|/ ____|
 *   | |  _ __ | |__  | (___    Institute of Embedded Systems
 *   | | | '_ \|  __|  \___ \   Zürcher Hochschule für Angewandte
 *  _| |_| | | | |____ ____) |  Wissenschaften
 * |_____|_| |_|______|_____/   8401 Winterthur, Switzerland
 *
 ****************************************************************************
 *
 * @version $Rev: 153 $
 * @author  $Author: bacr $
 * @date    $Date: 2013-10-10 17:25:34 +0200 (jeu., 10 oct. 2013) $
 *
 ***************************************************************************/

#ifndef __PTP2_SIGNALING_PACKET_H__
#define __PTP2_SIGNALING_PACKET_H__

#include "hcpp/ptp2_types.h"

typedef struct ptp2_signaling_header_t          ptp2_signaling_header_t;
typedef struct ptp2_signaling_packet_t          ptp2_signaling_packet_t;
typedef union  ptp2_tlv_t                       ptp2_tlv_t;
typedef struct ptp2_tlv_request_unicast_t       ptp2_tlv_request_unicast_t;
typedef struct ptp2_tlv_grant_unicast_t         ptp2_tlv_grant_unicast_t;
typedef struct ptp2_tlv_cancel_unicast_t        ptp2_tlv_cancel_unicast_t;
typedef struct ptp2_tlv_ack_cancel_unicast_t    ptp2_tlv_ack_cancel_unicast_t;

#define PTP2_SIGNALING_PACKET(obj) ((ptp2_signaling_packet_t *) obj)

#define PTP2_TLV_CHAINING_NUM                           4

/* length on the wire! */
/* header + tlv */
#define PTP2_SIGNALING_PACKET_TARGET_PORT_IDENT_LEN     10 /* target port identity  */
#define PTP2_TLV_MIN_LEN                                4  /* tlv type + tlv length */
#define PTP2_TLV_REQUEST_UNICAST_LEN                    6  /* only tlv value length! */
#define PTP2_TLV_GRANT_UNICAST_LEN                      8  /* only tlv value length! */
#define PTP2_TLV_CANCEL_UNICAST_LEN                     2  /* only tlv value length! */
#define PTP2_TLV_ACK_CANCEL_UNICAST_LEN                 2  /* only tlv value length! */

#define PTP2_SIGNALING_PACKET_OFFSET_TARGET_PORT_IDENT  0

#define PTP2_TLV_OFFSET_TYPE                            0
#define PTP2_TLV_OFFSET_LEN                             2

#define PTP2_TLV_REQUEST_UNICAST_OFFSET_TYPE            0
#define PTP2_TLV_REQUEST_UNICAST_OFFSET_LEN             2
#define PTP2_TLV_REQUEST_UNICAST_OFFSET_MSG_TYPE        4
#define PTP2_TLV_REQUEST_UNICAST_OFFSET_LOG_PERIOD      5
#define PTP2_TLV_REQUEST_UNICAST_OFFSET_DURATION        6

#define PTP2_TLV_GRANT_UNICAST_OFFSET_TYPE              0
#define PTP2_TLV_GRANT_UNICAST_OFFSET_LEN               2
#define PTP2_TLV_GRANT_UNICAST_OFFSET_MSG_TYPE          4
#define PTP2_TLV_GRANT_UNICAST_OFFSET_LOG_PERIOD        5
#define PTP2_TLV_GRANT_UNICAST_OFFSET_DURATION          6
#define PTP2_TLV_GRANT_UNICAST_OFFSET_UNUSED            10
#define PTP2_TLV_GRANT_UNICAST_OFFSET_RENEWAL           11

#define PTP2_TLV_CANCEL_UNICAST_OFFSET_TYPE             0
#define PTP2_TLV_CANCEL_UNICAST_OFFSET_LEN              2
#define PTP2_TLV_CANCEL_UNICAST_OFFSET_MSG_TYPE         4
#define PTP2_TLV_CANCEL_UNICAST_OFFSET_UNUSED           5

#define PTP2_TLV_ACK_CANCEL_UNICAST_OFFSET_TYPE         0
#define PTP2_TLV_ACK_CANCEL_UNICAST_OFFSET_LEN          2
#define PTP2_TLV_ACK_CANCEL_UNICAST_OFFSET_MSG_TYPE     4
#define PTP2_TLV_ACK_CANCEL_UNICAST_OFFSET_UNUSED       5

/* host byte-order */
#define PTP2_TLV_TYPE_MANAGEMENT                        0x0001
#define PTP2_TLV_TYPE_MANAGEMENT_ERROR_STATUS           0x0002
#define PTP2_TLV_TYPE_ORGANIZATION_EXTENSION            0x0003
#define PTP2_TLV_TYPE_REQUEST_UNICAST_TRANSMISSION      0x0004
#define PTP2_TLV_TYPE_GRANT_UNICAST_TRANSMISSION        0x0005
#define PTP2_TLV_TYPE_CANCEL_UNICAST_TRANSMISSION       0x0006
#define PTP2_TLV_TYPE_ACK_CANCEL_UNICAST_TRANSMISSION   0x0007
#define PTP2_TLV_TYPE_PATH_TRACE                        0x0008
#define PTP2_TLV_TYPE_ALTERNATE_TIME_OFFSET_INDICATOR   0x0009
#define PTP2_TLV_TYPE_AUTHENTICATION                    0x2000
#define PTP2_TLV_TYPE_AUTHENTICATION_CHALLENGE          0x2001
#define PTP2_TLV_TYPE_SECURITY_ASSOCIATION_UPDATE       0x2002
#define PTP2_TLV_TYPE_CUM_FREQ_SCALE_FACTOR_OFFSET      0x2003

/** request unicast transmission */
struct ptp2_tlv_request_unicast_t {
    uint16_t                type;           /**< 16.1.4.1.1 */
    uint16_t                len;            /**< 16.1.4.1.2 */
    union {
        uint8_t             raw;
        struct {
            uint8_t         unused : 4;
            uint8_t         type   : 4;     /**< 16.1.4.1.3 */
        };
    } msg;
    int8_t                  log_period;     /**< 16.1.4.1.4: requested period in seconds */
    uint32_t                duration;       /**< 16.1.4.1.5: requested duration in seconds */
};

/** grant unicast transmission */
struct ptp2_tlv_grant_unicast_t {
    uint16_t                type;           /**< 16.1.4.2.1 */
    uint16_t                len;            /**< 16.1.4.2.2 */
    union {
        uint8_t             raw;
        struct {
            uint8_t         unused : 4;
            uint8_t         type   : 4;     /**< 16.1.4.2.3 */
        };
    } msg;
    int8_t                  log_period;     /**< 16.1.4.2.4: granted period in seconds */
    uint32_t                duration;       /**< 16.1.4.2.5: granted duration in seconds*/
    uint8_t                 unused;
    union {
        uint8_t             raw;
        struct {
            uint8_t         flag   : 1;     /**< 16.1.4.2.6: first request (=0) or renewal (=1) */
            uint8_t         unused : 7;
        };
    } renewal;
};

/** cancel unicast transmission */
struct ptp2_tlv_cancel_unicast_t {
    uint16_t                type;
    uint16_t                len;
    union {
        uint8_t             raw;
        struct {
            uint8_t         unused : 4;
            uint8_t         type   : 4;
        };
    } msg;
    uint8_t                 unused;
};

/** acknowledge cancel unicast transmission */
struct ptp2_tlv_ack_cancel_unicast_t {
    uint16_t                type;
    uint16_t                len;
    union {
        uint8_t             raw;
        struct {
            uint8_t         unused : 4;
            uint8_t         type   : 4;
        };
    } msg;
    uint8_t                 unused;
};

union ptp2_tlv_t {
    struct {
        uint16_t                    type;
        uint16_t                    len;
    };
    ptp2_tlv_request_unicast_t      request_unicast;
    ptp2_tlv_grant_unicast_t        grant_unicast;
    ptp2_tlv_cancel_unicast_t       cancel_unicast;
    ptp2_tlv_ack_cancel_unicast_t   ack_cancel_unicast;
};

/* network byte order */
#define PTP2_SIGNALING_HEADER                                       \
    ptp2_port_identity_t                target_port_identity;       \
    uint8_t                             num_tlv;                    \
    ptp2_tlv_t                          tlv[PTP2_TLV_CHAINING_NUM];

/**
 * @struct ptp2_signaling_header_t
 * @brief  PTP header
 * @see    ip_packet_t
 *
 * @var    ptp2_signaling_header_t::tlv::type
 * @brief  14.1.1
 *
 * @var    ptp2_signaling_header_t::tlv::len
 * @brief  14.1.2
 */
struct ptp2_signaling_header_t {
    PTP2_SIGNALING_HEADER
};

/**
 * @struct ptp2_signaling_packet_t
 * @brief  PTP packet
 * @see    ptp2_signaling_packet_t
 *
 * @var    ptp2_signaling_packet_t::ether
 * @brief  Ethernet header
 *
 * @var    ptp2_signaling_packet_t::ip
 * @brief  IP header
 *
 * @var    ptp2_signaling_packet_t::udp
 * @brief  UDP header
 *
 * @var    ptp2_signaling_packet_t::ptp
 * @brief  PTPv2 header
 */
struct ptp2_signaling_packet_t {
    object_t            obj;
    info_header_t       info;
    ethernet_header_t   ether;      /**< @brief Ethernet header */
    ip_header_t         ip;         /**< @brief IP header */
    udp_header_t        udp;        /**< @brief UDP header */
    ptp2_header_t       ptp;        /**< @brief PTP header */
    PTP2_SIGNALING_HEADER
};

uint32_t    ptp2_signaling_packet_encode(ptp2_signaling_packet_t *this, raw_packet_t *raw_packet, uint32_t ptp2_signaling_offset);
void        ptp2_signaling_packet_decode(ptp2_signaling_packet_t *this, raw_packet_t *raw_packet, uint32_t ptp2_signaling_offset);

#endif

